/*
 * Copyright (C) 2015 备胎金服
 * 未经授权允许不得进行拷贝和修改
 *   http://www.btjf.com/
 */
package com.zhangman.framework.implementation;

import android.view.MotionEvent;
import android.view.View;
import com.zhangman.framework.Input;
import com.zhangman.framework.Pool;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhangman on 2016/11/1 13:37.
 * Email: zhangman523@126.com
 */
public class MultiTouchHandler implements TouchHandler {
  private static final int MAX_TOUCHPOINTS = 10;
  boolean[] isTouched = new boolean[MAX_TOUCHPOINTS];
  int[] touchX = new int[MAX_TOUCHPOINTS];
  int[] touchY = new int[MAX_TOUCHPOINTS];
  int[] id = new int[MAX_TOUCHPOINTS];
  Pool<Input.TouchEvent> touchEventPool;
  List<Input.TouchEvent> touchEvents = new ArrayList<>();
  List<Input.TouchEvent> touchEventBuffer = new ArrayList<>();
  float scaleX;
  float scaleY;

  public MultiTouchHandler(View view, float scaleX, float scaleY) {
    Pool.PoolObjectFactory<Input.TouchEvent> factory =
        new Pool.PoolObjectFactory<Input.TouchEvent>() {
          @Override public Input.TouchEvent createObject() {
            return new Input.TouchEvent();
          }
        };
    touchEventPool = new Pool<Input.TouchEvent>(100, factory);
    view.setOnTouchListener(this);
    this.scaleX = scaleX;
    this.scaleY = scaleY;
  }

  @Override public boolean isTouchDown(int pointer) {
    synchronized (this) {
      int index = getIndex(pointer);
      if (index < 0 || index >= MAX_TOUCHPOINTS) {
        return false;
      } else {
        return isTouched[index];
      }
    }
  }

  @Override public int getTouchX(int pointer) {
    synchronized (this) {
      int index = getIndex(pointer);
      if (index < 0 || index >= MAX_TOUCHPOINTS) {
        return 0;
      } else {
        return touchX[index];
      }
    }
  }

  @Override public int getTouchY(int pointer) {
    synchronized (this) {
      int index = getIndex(pointer);
      if (index < 0 || index >= MAX_TOUCHPOINTS) {
        return 0;
      } else {
        return touchY[index];
      }
    }
  }

  @Override public List<Input.TouchEvent> getTouchEvents() {
    synchronized (this) {
      int len = touchEvents.size();
      for (int i = 0; i < len; i++) {
        touchEventPool.free(touchEvents.get(i));
      }
      touchEvents.clear();
      touchEvents.addAll(touchEventBuffer);
      touchEventBuffer.clear();
      return touchEvents;
    }
  }

  private int getIndex(int pointerId) {
    for (int i = 0; i < MAX_TOUCHPOINTS; i++) {
      if (id[i] == pointerId) {
        return i;
      }
    }
    return -1;
  }

  @Override public boolean onTouch(View view, MotionEvent motionEvent) {
    synchronized (this) {
      int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
      int pointerIndex = (motionEvent.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)
          >> MotionEvent.ACTION_POINTER_ID_SHIFT;
      int pointerCount = motionEvent.getPointerCount();
      Input.TouchEvent touchEvent;
      for (int i = 0; i < MAX_TOUCHPOINTS; i++) {
        if (i >= pointerCount) {
          isTouched[i] = false;
          id[i] = -1;
          continue;
        }
        int pointerId = motionEvent.getPointerId(i);
        if (motionEvent.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) {
          continue;
        }
        switch (action) {
          case MotionEvent.ACTION_DOWN:
          case MotionEvent.ACTION_POINTER_DOWN:
            touchEvent = touchEventPool.newObject();
            touchEvent.type = Input.TouchEvent.TOUCH_DOWN;
            touchEvent.pointer = pointerId;
            touchEvent.x = touchX[i] = (int) (motionEvent.getX(i) * scaleX);
            touchEvent.y = touchY[i] = (int) (motionEvent.getY(i) * scaleY);
            isTouched[i] = true;
            id[i] = pointerId;
            touchEventBuffer.add(touchEvent);
            break;
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_POINTER_UP:
          case MotionEvent.ACTION_CANCEL:
            touchEvent = touchEventPool.newObject();
            touchEvent.type = Input.TouchEvent.TOUCH_UP;
            touchEvent.pointer = pointerId;
            touchEvent.x = touchX[i] = (int) (motionEvent.getX(i) * scaleX);
            touchEvent.y = touchY[i] = (int) (motionEvent.getY(i) * scaleY);
            isTouched[i] = false;
            id[i] = -1;
            touchEventBuffer.add(touchEvent);
            break;
          case MotionEvent.ACTION_MOVE:
            touchEvent = touchEventPool.newObject();
            touchEvent.type = Input.TouchEvent.TOUCH_DRAGGED;
            touchEvent.pointer = pointerId;
            touchEvent.x = touchX[i] = (int) (motionEvent.getX(i) * scaleX);
            touchEvent.y = touchY[i] = (int) (motionEvent.getY(i) * scaleY);
            isTouched[i] = true;
            id[i] = pointerId;
            touchEventBuffer.add(touchEvent);
            break;
        }
      }
      return true;
    }
  }
}
